package io.cici.cc.mybatis.lite.builder.xml;

import io.cici.cc.mybatis.lite.builder.Builder;
import io.cici.cc.mybatis.lite.builder.BuilderException;
import io.cici.cc.mybatis.lite.parse.Node;
import io.cici.cc.mybatis.lite.parse.XPathParser;
import io.cici.cc.mybatis.lite.session.Configuration;

import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;


public class XmlConfigBuilder extends Builder {
    private final XPathParser xPathParser;
    private boolean parsed;
    private final String environment;


    public XmlConfigBuilder(Reader reader, String environment, Properties props) {
        this(Configuration.class, reader, environment, props);
    }

    public XmlConfigBuilder(Class<? extends Configuration> configClass, Reader reader, String environment,
                            Properties props) {
        this(configClass, new XPathParser(reader, props, true, new XmlMapperEntityResolver()), environment, props);
    }


    public XmlConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(Configuration.class, inputStream, environment, props);
    }

    public XmlConfigBuilder(Class<? extends Configuration> configClass, InputStream inputStream, String environment,
                            Properties props) {
        this(configClass, new XPathParser(inputStream, props, true, new XmlMapperEntityResolver()), environment, props);
    }

    private XmlConfigBuilder(Class<? extends Configuration> configClass, XPathParser xPathParser, String environment,
                             Properties props) {
        super(new Configuration());

        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.xPathParser = xPathParser;
    }


    public Configuration parse() {
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        parseConfiguration(xPathParser.evalNode("/configuration"));
        return configuration;
    }

    private void parseConfiguration(Node node) {
        try {
            mapperElement(node.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }


    private void mapperElement(Node node) throws Exception {
        if (node == null) {
            return;
        }
        for (Node childNode : node.getChildNodeList()) {
            if ("package".equals(childNode.getName())) {
                String mapperPackage = childNode.getStringAttribute("name");
                configuration.addMappers(mapperPackage);
            } else {
                String resource = childNode.getStringAttribute("resource");
                String mapperClass = childNode.getStringAttribute("class");
                if (resource != null) {

                    try (InputStream inputStream = null) {
                        XmlMapperBuilder mapperParser = new XmlMapperBuilder(inputStream, configuration, resource, configuration.getSqlIdToNodeMap());
                        mapperParser.parse();
                    }
                } else if (mapperClass != null) {
                    Class<?> mapperInterface = Class.forName(mapperClass);
                    configuration.addMapper(mapperInterface);
                } else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }

}
